import os

import inspect

import hashlib

class __File__:
    def __init__(self, file_path:str, file_name:str=None):
        if file_path is None: raise ValueError('required parameter file_path can not be None.')
        if not isinstance(file_path, str):
            raise ValueError('parameter file_path must be a string value.')
        self.__file_path__ = file_path

        if file_name is None: # 从file_path中提取file_name
            self.__file_name__ = os.path.basename(file_path)
        else:
            if not isinstance(file_name, str):
                raise ValueError('parameter file_name must be a string value or None.')
            if self.__file_path__.endswith(file_name):
                self.__file_name__ = file_name
            else:
                raise ValueError('file path must be a full file path')

    def exist(self) -> bool:
        return os.path.exists(self.__file_path__)

    def is_file(self) -> bool:
        return os.path.isfile(self.__file_path__)

    def is_directory(self) -> bool:
        return os.path.isdir(self.__file_path__)

    # /directory/xxx.ext
    def file_path(self) -> str:
        return self.__file_path__

    # /directory
    def dir_path(self) -> str:
        return os.path.dirname(self.__file_path__)

    # Directory[/directory]
    def dir(self) -> str:
        return Directory(self.dir_path())

    # xxx.ext
    def file_name(self) -> str:
        return self.__file_name__

    def print(self):
        print()
        print('exist                :', self.exist())
        print('is file              :', self.is_file())
        print('is directory         :', self.is_directory())
        print('file_path            :', self.file_path())
        print('dir_path             :', self.dir_path())
        print('file_name            :', self.file_name())


class File(__File__):
    def print(self):
        super().print()

        print('ext                  :', self.ext())
        print('ext_no_dot           :', self.ext_no_dot())
        print('file_name_without_ext:', self.file_name_without_ext())

    # .ext
    def ext(self) -> str:
        return os.path.splitext(self.file_path())[-1].lower()

    # ext
    def ext_no_dot(self) -> str:
        ext = self.ext()
        return '' if ext == '' else ext[1:]

    # xxx
    def file_name_without_ext(self) -> str:
        return os.path.splitext(self.file_name())[0]

    def md5(self) -> str:
        if not self.exist(): raise ValueError(f'can not get MD5 from {self.file_path()}, cause it does not exist.')
        with open(self.file_path(), 'rb') as file:
            md5 = hashlib.md5()
            while True:
                data = file.read(4096)
                if not data:
                    break
                md5.update(data)
            return md5.hexdigest()

class Directory(__File__):
    def print(self):
        super().print()

    # True or False
    def empty(self) -> bool:
        return not os.listdir(self.file_path())

    # Directory[{self_directory}/{file_name}]
    def sub_dir(self, file_name):
        if not isinstance(file_name, str):
            raise ValueError('parameter file name must be a string value.')
        return Directory(os.path.join(self.file_path(), file_name))

    # File[{self_directory}/{file_name}]
    def sub_file(self, file_name):
        if not isinstance(file_name, str):
            raise ValueError('parameter file name must be a string value.')
        return File(os.path.join(self.file_path(), file_name))

    def mkdirs(self):
        os.makedirs(self.file_path(), exist_ok=True)

def for_directory___default___function_if_other(directory, file_path, file_name):
    print(file_name, 'is neither a file nor a folder in ', directory)

def for_directory(
    directory,
    function_if_file = None,
    function_if_directory = lambda directory, dir: (),
    function_if_other = for_directory___default___function_if_other
):
    if isinstance(directory, Directory):
        directory = directory.file_path()
    if not isinstance(directory, str):
        raise ValueError('parameter directory must be a string value or a Directory.')
    files = os.listdir(directory)
    for file_name in files:
        file_path = os.path.join(directory, file_name)
        if os.path.isfile(file_path):
            if function_if_file is not None:
                function_if_file(directory, File(file_path, file_name))
        elif os.path.isdir(file_path):
            if function_if_directory is not None:
                function_if_directory(directory, Directory(file_path, file_name))
        else:
            if function_if_other is not None:
                function_if_other(directory, file_path, file_name)

import sys
from kinyx.python_util import module_exists
from kinyx.command_util import input_str
from typing import List

def select_directory(
    title               : str           = None,
    exit_on_blank       : bool          = True,
    initialdir          : str           = None,
):
    title = '选择文件夹...' if title is None else title

    if module_exists('tkinter'):
        from tkinter import filedialog
        result = filedialog.askdirectory(
            title           = title,
            initialdir      = initialdir,
        )
    else:
        result = input_str(
            required        = False,
            input_info      = title,
            default_value   = initialdir,
        )

    if result is None or result == '':
        if exit_on_blank: sys.exit()
        else: return None
    return result

def select_file(
    title               : str           = None,
    exit_on_blank       : bool          = True,
    filetypes           : List[str]     = None,
):
    title = '选择文件...' if title is None else title

    if module_exists('tkinter'):
        from tkinter import filedialog
        result = filedialog.askopenfilename(
            title           = title,
            filetypes       = filetypes if filetypes is not None else [],
        )
    else:
        result = input_str(
            required        = False,
            input_info      = title,
            endswith        = [tup[1].lstrip('*').rstrip('.') for tup in list_of_tuples] if filetypes is not None else None,
        )

    if result is None or result == '':
        if exit_on_blank: sys.exit()
        else: return None
    return result
